package algorithm;

import entity.Slot;
import entity.Student;
import java.util.List;
import util.Debug;

public class TabuSearch {

    /**
     * @param args
     */
    private Data D;
    List<Student> students;
    int nbStudents;
    VarInt[] var_slots;
    boolean[][] conflict;
    int minSlot;
    int maxSlot;
    List<Slot> slots;

    public TabuSearch(Data data) {
        D = data;
        students = D.getStudents();
        slots = D.getSlots();
        nbStudents = students.size();
        var_slots = new VarInt[nbStudents];
        conflict = new boolean[nbStudents][nbStudents];

        for (int i = 0; i < nbStudents; i++) {
            for (int j = 0; j < nbStudents; j++) {
                Student si = students.get(i);
                Student sj = students.get(j);
                if (i != j) {
                    conflict[i][j] = D.conflictStudents(si, sj);
                } else {
                    conflict[i][j] = false;
                }
            }
        }

        minSlot = 10000;
        maxSlot = -1;

        for (int i = 0; i < slots.size(); i++) {
            Slot sl = slots.get(i);
            if (minSlot > sl.getID()) {
                minSlot = sl.getID();
            }
            if (maxSlot < sl.getID()) {
                maxSlot = sl.getID();
            }
        }

        //maxSlot = 10;
        for (int i = 0; i < nbStudents; i++) {
            var_slots[i] = new VarInt(i, minSlot, maxSlot);
        }

    }

    public void localsearch(int maxIter) {
        ConstraintSystem CS = new ConstraintSystem();

        for (int i = 0; i < nbStudents - 1; i++) {
            for (int j = i + 1; j < nbStudents; j++) {
                if (conflict[i][j]) {
                    VarInt vari = var_slots[i];
                    VarInt varj = var_slots[j];
                    CS.post(new NotEqual(vari, varj));
//                    System.out.println("post(" + i + "," + j + ")");
                }
            }
        }

        CS.close();

        for (int i = 0; i < nbStudents; i++) {
            var_slots[i].setValue(minSlot);
        }

        System.out.println("violations = " + CS.violations());

        int[][] tabu = new int[nbStudents][maxSlot + 1];

        for (int i = 0; i < nbStudents; i++) {
            for (int sl = minSlot; sl <= maxSlot; sl++) {
                tabu[i][sl] = -1;
            }
        }

        int tbl = 20;

        int it = 1;
        int bestV = CS.violations();
        int oldV = CS.violations();

        while (it < maxIter && bestV > 0) {
            int minDelta = 1000000;
            int sel_i = -1;
            int sel_sl = -1;
            for (int i = 0; i < nbStudents; i++) {
                for (int sl = minSlot; sl <= maxSlot; sl++) {
                    int d = CS.getAssignDelta(var_slots[i], sl);
                    if (CS.violations() + d < bestV || tabu[i][sl] < it) {
                        if (d < minDelta) {
                            minDelta = d;
                            sel_i = i;
                            sel_sl = sl;
                        }
                    }
                }
            }

            System.out.println("sel_i = " + sel_i + " sel_sl = " + sel_sl);

            CS.propagate(var_slots[sel_i], sel_sl);
            var_slots[sel_i].setValue(sel_sl);

            tabu[sel_i][sel_sl] = it + tbl;

            if (oldV + minDelta != CS.violations()) {
                System.out.println("Error, oldV = " + oldV + " delta = " + minDelta + " violations = " + CS.violations());
                return;
            }
            if (!CS.verify()) {
                System.out.println("NOT verify");
                return;
            }
            oldV = CS.violations();
            if (CS.violations() < bestV) {
                bestV = CS.violations();
            }
            System.out.println("Step " + it + " : Assign var_slots[" + sel_i + "] to " + sel_sl + " violations = " + CS.violations() + " best = " + bestV);
            it++;
        }
        for (int i = 0; i < var_slots.length; i++) {
            Debug.d(i + ":" + var_slots[i].getValue());
            Student student = students.get(i);
            student.setSlotId(var_slots[i].getValue());
        }
    }
}
